/*
*    Phantom v2.0 GUI Automation Software
*    Copyright (C) 2009  John C. Van Arsdall (john@phantomtest.com)
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License along
*    with this program; if not, write to the Free Software Foundation, Inc.,
*    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
// PhantomBase.cpp : Defines the initialization routines for the DLL.
//

#include "stdafx.h"
#include "PhantomBase.h"
#include "ClassWindow.h"
#include "DataRegKey.h"
#include "GlobalFuncs.h"
#include "SSI_StdFuncsExport.h"
#include "Minion_External.h"
#include "Mmsystem.h"
#include "WindowChecker.h"

#include "DllFunc.h"

#include <direct.h>
#include <shellapi.h>
#include <io.h>

#define __OLD_WIN__

#ifndef __OLD_WIN__
  #include <psapi.h>
#endif

#ifdef _DEBUG
#define new DEBUG_NEW
#endif
//                       MMmmss
// Will be '2.0.0.0' or '200000' ??
#define PHANTOM_VERSION "2.1"
#define MAX_DLL_PARAMS 16

CDataFile PHANTOM_LogFile;
DllTree PHANTOM_DllList;

typedef int (*FUNCPROC)(CEngine*); 

//CStatus AddChildren(DataNode CurrNode, CDataJavaWindow* ParWin);

SSI_BOOL GetInput(String& Prompt, SSI_FunctionAPI& theAPI, String& Ret);

CStatus MouseMove(SSI_INT64 X, SSI_INT64 Y){
  int sV = GetSystemMetrics(SM_CYSCREEN);
	int sH = GetSystemMetrics(SM_CXSCREEN);

	X = int(double(double(X) / double(sH)) * (double)65535);
	Y = int(double(double(Y) / double(sV)) * (double)65535);

  mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, X, Y, 0, 0);
	return STAT_OK;
}

CStatus MouseClick(SSI_INT64 iType, SSI_INT64 X, SSI_INT64 Y, SSI_INT64 iButton){
  CStatus RetStat;

  if((iType < PM_CLICK) || (iType > PM_DBLCLICK))
	{
		RetStat.Format(sERROR, "MouseClick parameter 4 must be 0,1,2, or 3");
		return RetStat;
	}

  int sV = GetSystemMetrics(SM_CYSCREEN);
	int sH = GetSystemMetrics(SM_CXSCREEN);

	X = int(double(double(X) / double(sH)) * (double)65535);
	Y = int(double(double(Y) / double(sV)) * (double)65535);

  POINT Pt;
  Pt.x = X;
  Pt.y = Y;

	DWORD FLAG = MOUSEEVENTF_ABSOLUTE;
	FLAG = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
	mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		

	FLAG = MOUSEEVENTF_ABSOLUTE;	
	
	if(iButton == PM_LEFT)
	{
		if(iType == PM_CLICK)
		{
			FLAG = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE;;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
			FLAG = MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
		}
		else if(iType == PM_DOWN)
		{
			FLAG |= MOUSEEVENTF_LEFTDOWN;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
		}
		else if(iType == PM_UP)
		{
			FLAG |= MOUSEEVENTF_LEFTUP;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
		}
		else if(iType == PM_DBLCLICK)
		{
			FLAG = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE;;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
			FLAG = MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
			
			FLAG = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE;;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
			FLAG = MOUSEEVENTF_LEFTUP | MOUSEEVENTF_ABSOLUTE;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
		}
	}
	else if(iButton == PM_RIGHT)
	{
		if(iType == PM_CLICK)
		{
			FLAG = MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_ABSOLUTE;;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
			FLAG = MOUSEEVENTF_RIGHTUP | MOUSEEVENTF_ABSOLUTE;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);
		}
		else if(iType == PM_DOWN)
		{
			FLAG |= MOUSEEVENTF_RIGHTDOWN;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
		}
		else if(iType == PM_UP)
		{
			FLAG |= MOUSEEVENTF_RIGHTUP;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
		}
		else if(iType == PM_DBLCLICK)
		{
			FLAG = MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_ABSOLUTE;;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
			FLAG = MOUSEEVENTF_RIGHTUP | MOUSEEVENTF_ABSOLUTE;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);
			
			FLAG = MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_ABSOLUTE;;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
			FLAG = MOUSEEVENTF_RIGHTUP | MOUSEEVENTF_ABSOLUTE;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);
		}
	}
	else if(iButton == PM_MID)
	{
		if(iType == PM_CLICK)
		{
			FLAG = MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_ABSOLUTE;;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
			FLAG = MOUSEEVENTF_MIDDLEUP | MOUSEEVENTF_ABSOLUTE;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);
		}
		else if(iType == PM_DOWN)
		{
			FLAG |= MOUSEEVENTF_MIDDLEDOWN;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
		}
		else if(iType == PM_UP)
		{
			FLAG |= MOUSEEVENTF_MIDDLEUP;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
		}
		else if(iType == PM_DBLCLICK)
		{
			FLAG = MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_ABSOLUTE;;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
			FLAG = MOUSEEVENTF_MIDDLEUP | MOUSEEVENTF_ABSOLUTE;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);

			FLAG = MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_ABSOLUTE;;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);		
			FLAG = MOUSEEVENTF_MIDDLEUP | MOUSEEVENTF_ABSOLUTE;
			mouse_event(FLAG, Pt.x, Pt.y, SSI_NULL, SSI_NULL);
		}
	}else{
		RetStat.Format(sERROR, "MouseClick parameter 1 must be 0,1, or 2");
		return RetStat;
	}
	return STAT_OK;

}


SSI_INT64 CountTabs(String& inStr){
	SSI_INT64 ctr = 0;
	while(inStr.GetAt(ctr) == '\t'){
		ctr++;
	}
	return ctr;
}

void PhantomKeywordProcessor(SSI_KeywordAPI& theAPI);

//
//	Note!
//
//		If this DLL is dynamically linked against the MFC
//		DLLs, any functions exported from this DLL which
//		call into MFC must have the AFX_MANAGE_STATE macro
//		added at the very beginning of the function.
//
//		For example:
//
//		extern "C" SSI_BOOL PASCAL EXPORT ExportedFunction()
//		{
//			AFX_MANAGE_STATE(AfxGetStaticModuleState());
//			// normal function body here
//		}
//
//		It is very important that this macro appear in each
//		function, prior to any calls into MFC.  This means that
//		it must appear as the first statement within the 
//		function, even before any object variable declarations
//		as their constructors may generate calls into the MFC
//		DLL.
//
//		Please see MFC Technical Notes 33 and 58 for additional
//		details.
//

// CPhantomBaseApp
/*
BEGIN_MESSAGE_MAP(CPhantomBaseApp, CWinApp)
END_MESSAGE_MAP()


// CPhantomBaseApp construction

CPhantomBaseApp::CPhantomBaseApp()
{
	// TODO: add construction code here,
	// Place all significant initialization in InitInstance
}


// The one and only CPhantomBaseApp object

CPhantomBaseApp theApp;

// CPhantomBaseApp initialization

BOOL CPhantomBaseApp::InitInstance()
{
	CWinApp::InitInstance();

	return 1;
}
*/

//BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID fImpLoad)
//{
//	if(fdwReason == DLL_PROCESS_ATTACH)
//	{
//		int i = 0;
		//g_hinstDll = hinstDll;
//	}
//	else if(fdwReason == DLL_PROCESS_DETACH)
//	{
		//if(g_hHook != SSI_NULL)
		//{
		//	UnhookWindowsHookEx(g_hHook);
		//	g_hHook = SSI_NULL;
		//}
//	}	
//	return TRUE;
//}



int g_LoadCount = 0;
SSI_INT64 CDataWindow::sType = 0;
SSI_INT64 CDataWindow::iDelay = 200;
SSI_INT64 CDataRegKey::sType = 0;

void RegisterFunctions(CEngine* anEngine){
	g_LoadCount++;
	CDataWindow aWind;
	CDataRegKey aRegKey;

	CKeywordDecl aDecl;
	CFunctionDecl aNewFunc;

	anEngine->SetPackageName("Phantom");

	// Keywords
	aDecl.m_Keyword = "use";
	aDecl.theProcessor = (ProcessingKeywordPtr)PhantomKeywordProcessor;
	anEngine->AddKeyword(aDecl);

	aDecl.m_Keyword = "define";
	aDecl.theProcessor = (ProcessingKeywordPtr)PhantomKeywordProcessor;
	anEngine->AddKeyword(aDecl);

	aDecl.m_Keyword = "dll";
	aDecl.theProcessor = (ProcessingKeywordPtr)PhantomKeywordProcessor;
	anEngine->AddKeyword(aDecl);


	// Functions
	aNewFunc.Clear();
	aNewFunc.Name = "Sleep";
	aNewFunc.addType(CDataReal::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "Sleep";
	aNewFunc.addType(CDataInt::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "Log";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "LogError";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "LogWarning";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "Version";
	aNewFunc.ReturnType = CDataString::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "Exit";
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "SetDelay";
	aNewFunc.addType(CDataInt::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "ListWindow";
	aNewFunc.addType(CDataWindow::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	/////////////////////////////////////////////////////
	// Process Functions
	aNewFunc.Clear();
	aNewFunc.Name = "GetProcessID";
	aNewFunc.ReturnType = CDataInt::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);
	
  aNewFunc.Clear();
	aNewFunc.Name = "GetProcessID";
	aNewFunc.addType(CDataWindow::GetRegisteredType());
	aNewFunc.ReturnType = CDataInt::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

#ifndef __OLD_WIN__
	aNewFunc.Clear();
	aNewFunc.Name = "GetProcessMemory";
	aNewFunc.addType(CDataInt::GetRegisteredType());
	aNewFunc.ReturnType = CDataInt::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);
#endif

	aNewFunc.Clear();
	aNewFunc.Name = "KillProcess";
	aNewFunc.addType(CDataInt::GetRegisteredType());
	aNewFunc.ReturnType = CDataBool::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	/////////////////////////////////////////////////////
	// Clipboard Functions
	aNewFunc.Clear();
	aNewFunc.Name = "GetClipboardText";
	aNewFunc.ReturnType = CDataString::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "SetClipboardText";
  aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	/////////////////////////////////////////////////////
	// Window Access Functions
	aNewFunc.Clear();
	aNewFunc.Name = "Exists";
	aNewFunc.addType(CDataWindow::GetRegisteredType());
	aNewFunc.ReturnType = CDataBool::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "WaitForWindow";
	aNewFunc.addType(CDataWindow::GetRegisteredType());
	aNewFunc.addType(CDataInt::GetRegisteredType());
	aNewFunc.ReturnType = CDataBool::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

//	aNewFunc.Clear();
//	aNewFunc.Name = "GetActiveWindow";
//  aNewFunc.ReturnType = CDataWindow::GetRegisteredType();
//	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
//	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "GetDesktopWindow";
  aNewFunc.ReturnType = CDataWindow::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "GetForegroundWindow";
  aNewFunc.ReturnType = CDataWindow::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	/////////////////////////////////////////////////////
	// Deprecated String Functions
	aNewFunc.Clear();
	aNewFunc.Name = "GetLength";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataInt::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "Find";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataInt::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "SubString";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.addType(CDataInt::GetRegisteredType());
	aNewFunc.addType(CDataInt::GetRegisteredType());
	aNewFunc.ReturnType = CDataString::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "Concat";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataString::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "IntToStr";
	aNewFunc.addType(CDataInt::GetRegisteredType());
	aNewFunc.ReturnType = CDataString::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "StrToInt";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataInt::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "RealToStr";
	aNewFunc.addType(CDataReal::GetRegisteredType());
	aNewFunc.ReturnType = CDataString::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "StrToReal";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataReal::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	/////////////////////////////////////////////////////
	// Time Functions
	aNewFunc.Clear();
	aNewFunc.Name = "GetTime";
	aNewFunc.ReturnType = CDataString::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "GetDate";
	aNewFunc.ReturnType = CDataString::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "GetDayOfWeek";
	aNewFunc.ReturnType = CDataInt::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	/////////////////////////////////////////////////////
	// Misc Functions
	aNewFunc.Clear();
	aNewFunc.Name = "TypeKeys";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "KeyUp";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

 	aNewFunc.Clear();
	aNewFunc.Name = "KeyDown";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);
  
  aNewFunc.Clear();
	aNewFunc.Name = "MouseMove";
	aNewFunc.addType(CDataInt::GetRegisteredType());
	aNewFunc.addType(CDataInt::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "MouseClick";
	aNewFunc.addType(CDataInt::GetRegisteredType());
	aNewFunc.addType(CDataInt::GetRegisteredType());
	aNewFunc.addType(CDataInt::GetRegisteredType());
	aNewFunc.addType(CDataInt::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "InputInt";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataInt::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "InputReal";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataReal::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "InputString";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataString::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "PrintMessage";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "Alert";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "LoadPackageDLL";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "PlaySound";
  aNewFunc.ReturnType = CDataBool::GetRegisteredType();
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);


	/////////////////////////////////////////////////////
	// System Functions
	aNewFunc.Clear();
	aNewFunc.Name = "System";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "Run";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataBool::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "ChangeDir";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "GetMachineName";
	aNewFunc.ReturnType = CDataString::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "GetDirContents";
	aNewFunc.ReturnType = CDataString::GetRegisteredType();
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "GetCurrentDir";
	aNewFunc.ReturnType = CDataString::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "MakeDir";
	aNewFunc.ReturnType = CDataBool::GetRegisteredType();
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "GetUserName";
	aNewFunc.ReturnType = CDataString::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "CopyFile";
	aNewFunc.ReturnType = CDataBool::GetRegisteredType();
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "CopyDir";
	aNewFunc.ReturnType = CDataBool::GetRegisteredType();
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "DeleteFile";
	aNewFunc.ReturnType = CDataBool::GetRegisteredType();
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "DeleteDir";
	aNewFunc.ReturnType = CDataBool::GetRegisteredType();
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);


	aNewFunc.Clear();
	aNewFunc.Name = "MainWin";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataWindow::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "MainWin";
	aNewFunc.addType(CDataInt::GetRegisteredType());
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataWindow::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "WriteFile";
	aNewFunc.addType(CDataFile::GetRegisteredType());
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataBool::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "OpenFile";
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataFile::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "OpenFile";
  aNewFunc.addType(CDataFile::GetRegisteredType());
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataBool::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "CloseFile";
	aNewFunc.addType(CDataFile::GetRegisteredType());
	aNewFunc.ReturnType = CDataBool::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "ReadFile";
	aNewFunc.addType(CDataFile::GetRegisteredType());
	aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataBool::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "ReadAllFile";
	aNewFunc.addType(CDataFile::GetRegisteredType());
	aNewFunc.ReturnType = CDataString::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);

	aNewFunc.Clear();
	aNewFunc.Name = "FindFile";
	aNewFunc.addType(CDataString::GetRegisteredType());
  aNewFunc.addType(CDataString::GetRegisteredType());
	aNewFunc.ReturnType = CDataString::GetRegisteredType();
	aNewFunc.SetProcessor((ProcessingFunctionPtr)StandardFunctionProcessor);
	anEngine->AddFunction(aNewFunc);
}

void CleanUp(CEngine* anEngine){

	g_LoadCount--;
	if(g_LoadCount <= 0){
		// D'oh... this has to be before ClearFunctions, else functions will
		// just be re-initialized
		CDataWindow aWind;
		// Do this to force re-init of data
    // This will also clear the minion log
		Factory_RemoveData(aWind.Type);

		CWindowChecker::ClearFunctions();
		g_LoadCount = 0;
	}
}

void StandardFunctionProcessor(SSI_FunctionAPI& theAPI){
	if(theAPI.m_FunctionName == "Sleep"){
		if(theAPI.GetParameterType(0) == CDataReal::GetRegisteredType()){
			CDataReal r;
			r.Data = SSI_EXTRACT(CDataReal, 0);
			PhantomSleep(r.Data);
		}else{
			CDataInt i;
			i.Data = SSI_EXTRACT(CDataInt, 0);
			PhantomSleep(double(i.Data));
		}
	}else	if(theAPI.m_FunctionName == "Log"){
		CDataString s;
		s.Data = SSI_EXTRACT(CDataString, 0);
		theAPI.m_Engine->GetOutStream()->Disp(s.Data.Str(), 1);
    theAPI.m_Engine->Log(s.Data);
	}else	if(theAPI.m_FunctionName == "LogError"){
		CDataString s;
		s.Data = SSI_EXTRACT(CDataString, 0);
		theAPI.m_ReturnStatus = CStatus(sERROR, s.Data);
	}else	if(theAPI.m_FunctionName == "LogWarning"){
		CDataString s;
		s.Data = SSI_EXTRACT(CDataString, 0);
		theAPI.m_ReturnStatus = CStatus(sWARNING, s.Data);
	}else	if(theAPI.m_FunctionName == "Version"){
		CDataString s;
		s.Data = PHANTOM_VERSION;
		theAPI.SetReturnData(&s);
		theAPI.m_ReturnStatus = STAT_OK;

	/////////////////////////////////////////////////////
	// Window Access Functions
	}else	if(theAPI.m_FunctionName == "Exists"){
		CDataWindow* w;
		CDataBool bRet;
		w = SSI_EXTRACT_D(CDataWindow, 0);
		unsigned int Val;
		HMENU hMenu;
		if(w->FindMe(Val, hMenu)){
			bRet.Data = 1;
		}else{
			bRet.Data = 0;
		}
		theAPI.m_ReturnStatus = STAT_OK;
		theAPI.SetReturnData(&bRet);
	}else	if(theAPI.m_FunctionName == "WaitForWindow"){
		CDataWindow* w;
		CDataBool bRet;
		SSI_INT64 iS;
		SSI_INT64 ctr = 0;
		w = SSI_EXTRACT_D(CDataWindow, 0);
		iS = SSI_EXTRACT(CDataInt, 1);
		unsigned int Val;
		HMENU hMenu;

		while(1){
			if(w->FindMe(Val, hMenu)){
				bRet.Data = 1;
				break;
			}else{
				bRet.Data = 0;
				ctr++;
				if(ctr > iS){
					break;
				}else{
					Sleep(1000);
				}
			}
		}
		theAPI.m_ReturnStatus = STAT_OK;
		theAPI.SetReturnData(&bRet);
//	}else	if(theAPI.m_FunctionName == "GetActiveWindow"){
//    CDataWindow Ret;
//    Ret.InitializeFromWindow(CWnd::GetActiveWindow());
//    //CWnd aWin;
//    //HWND Active = GetActiveWindow();
//    //if(Active){
//    //  if(aWin.Attach(Active)){
//        //Ret.InitializeFromWindow(&aWin);
//        
//    //    aWin.Detach();
//    //  }
//    //}
//    theAPI.SetReturnData(&Ret);
  }else	if(theAPI.m_FunctionName == "GetDesktopWindow"){
    CDataWindow Ret;
    CWnd aWin;
    HWND Active = GetDesktopWindow();
    if(Active){
      if(aWin.Attach(Active)){
        Ret.InitializeFromWindow(&aWin);
        aWin.Detach();
      }
    }
    theAPI.SetReturnData(&Ret);
  }else	if(theAPI.m_FunctionName == "GetForegroundWindow"){
    CDataWindow Ret;
    CWnd aWin;
    HWND Active = GetForegroundWindow();
    if(Active){
      if(aWin.Attach(Active)){
        Ret.InitializeFromWindow(&aWin);
        aWin.Detach();
      }
    }
    theAPI.SetReturnData(&Ret);
	}else	if(theAPI.m_FunctionName == "Exit"){
		CInterpreter* Interp = theAPI.m_Engine->GetInterpreter();
		if(!Interp){
			theAPI.m_ReturnStatus = STAT_ERR;  // Internal Error... should have an interpreter
		}else{
			Interp->SetStopFlag();
		}
	}else	if(theAPI.m_FunctionName == "SetDelay"){
		CDataInt i;
		i.Data = SSI_EXTRACT(CDataInt, 0);
		theAPI.m_ReturnStatus = CDataWindow::SetDelay(i.Data);
	}else	if(theAPI.m_FunctionName == "ListWindow"){
		CDataWindow* aWin = 0;
		aWin = (CDataWindow*)(CData*)*(theAPI[0]);
		if(!aWin) return;
		aWin->ListToScreen(theAPI.m_Engine, 0);
		return;
	// Process Functions -------------------------------------
	}else	if(theAPI.m_FunctionName == "GetProcessID"){
		CDataInt i;
		if(theAPI.GetNumParams() == 0){
			i.Data = (SSI_INT64)GetCurrentProcessId();
		}else{
			CDataWindow* aWin;
			aWin = (CDataWindow*)(CData*)*(theAPI[0]);

			HMENU hMenu;
			UINT Val;
			if(aWin->FindMe(Val, hMenu)){
				DWORD tID = GetWindowThreadProcessId(aWin->myWin, (LPDWORD)(&(i.Data)));
		  }else{
			  i.Data = 0;
		  }
		}
		theAPI.SetReturnData(&i);
		return;
#ifndef __OLD_WIN__
	}else	if(theAPI.m_FunctionName == "GetProcessMemory"){
		CDataInt i;
		i.Data = SSI_EXTRACT(CDataInt, 0);
		HANDLE hProcess = 0;
    PROCESS_MEMORY_COUNTERS pmc;
		hProcess = OpenProcess(  PROCESS_QUERY_INFORMATION |
                                    PROCESS_VM_READ,
																		FALSE, i.Data );

		if (NULL == hProcess){
			i.Data = 0;
			theAPI.SetReturnData(&i);
      P_MAKEERR0(ERR_P00500);
			return;
		}
		if (!GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) ){
			i.Data = 0;
			theAPI.SetReturnData(&i);
      P_MAKEERR0(ERR_P00501);
			return;
		}
        
    CloseHandle( hProcess );
		i.Data =  (SSI_INT64)pmc.WorkingSetSize;
		theAPI.SetReturnData(&i);
		return;
#endif
	}else	if(theAPI.m_FunctionName == "KillProcess"){
		CDataInt i;
		i.Data = SSI_EXTRACT(CDataInt, 0);
		CDataBool b;
		HANDLE hProcess = 0;
		hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, i.Data);
		// This will not close cleanly... check http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q178893&
		// Still may want to use this, though.
		// Look up OpenProcess to see other process information that can be gotten
		b.Data = TerminateProcess(hProcess, 0);
    CloseHandle( hProcess );
		theAPI.SetReturnData(&b);
		return;
// Clipboard Functions -------------------------------------
	}else	if(theAPI.m_FunctionName == "GetClipboardText"){
		char * buffer = 0;
	  if(OpenClipboard(NULL)){
  		buffer = (char*)GetClipboardData(CF_TEXT);
	  }
    CDataString s;
    if(buffer){
      s.Data = buffer;
    }

    CloseClipboard();

    theAPI.SetReturnData(&s);

	}else	if(theAPI.m_FunctionName == "SetClipboardText"){
		String s = SSI_EXTRACT(CDataString, 0);
    if(OpenClipboard(NULL)){ 
		  HGLOBAL clipbuffer;
		  char * buffer;
		  EmptyClipboard();
		  clipbuffer = GlobalAlloc(GMEM_DDESHARE, s.GetLength()+1);
		  buffer = (char*)GlobalLock(clipbuffer);
      strcpy(buffer, s.Str());
		  GlobalUnlock(clipbuffer);
		  SetClipboardData(CF_TEXT,clipbuffer);
		  CloseClipboard();
	  }  
	}else	if(theAPI.m_FunctionName == "WriteFile"){
		CDataFile* aFile = 0;
    CDataBool aBool;
		aFile = (CDataFile*)(CData*)*(theAPI[0]);
		if(!aFile) return;
		String s = SSI_EXTRACT(CDataString, 1);
		theAPI.m_ReturnStatus = aFile->Write(s);
    if(!theAPI.m_ReturnStatus){
      aBool.Data = 0;
    }else{
      aBool.Data = 1;
    }
    theAPI.SetReturnData(&aBool);

		return;
	}else	if(theAPI.m_FunctionName == "ReadFile"){
		CDataFile* aFile = 0;
		CDataString aString;
		CStatus RetStat;
		CDataBool aBool;
		aFile = (CDataFile*)(CData*)*(theAPI[0]);
		CVariable* aVar = theAPI[1];

		if(!aFile) return;
		String s = SSI_EXTRACT(CDataString, 1);
		theAPI.m_ReturnStatus = aFile->Read(s, aBool.Data);

		aString.Data = s;
		RetStat = aVar->SetWorkingData(&aString);
		if(!RetStat){
			theAPI.m_ReturnStatus = RetStat;
			return;
		}
		theAPI.SetReturnData(&aBool);
		RetStat = theAPI.m_Engine->SetVariable(*aVar);
		if(!RetStat){
			theAPI.m_ReturnStatus = RetStat;
			return;
		}

		return;
  }else	if(theAPI.m_FunctionName == "ReadAllFile"){
		CDataFile* aFile = 0;
		CDataString aString;
		CStatus RetStat;
		aFile = (CDataFile*)(CData*)*(theAPI[0]);

		if(!aFile) return;
    theAPI.m_ReturnStatus = aFile->ReadAll(aString.Data);

		theAPI.SetReturnData(&aString);
		return;
	}else	if(theAPI.m_FunctionName == "CloseFile"){
		CDataFile* aFile = 0;
		aFile = (CDataFile*)(CData*)*(theAPI[0]);
		if(!aFile) return;
		theAPI.m_ReturnStatus = aFile->Close();
		return;
	}else	if(theAPI.m_FunctionName == "OpenFile"){

    String Name;
    String Access;
    CDataFile* aFile;
    CVariable* aVar;
    SSI_INT64 cnt = theAPI.GetNumParams();
    if(cnt == 3){
      aVar   = theAPI[0];
      aFile  = (CDataFile*)&aVar->GetWorkingData();
      Name   = SSI_EXTRACT(CDataString, 1);
		  Access = SSI_EXTRACT(CDataString, 2);
    }else{
      Name   = SSI_EXTRACT(CDataString, 0);
		  Access = SSI_EXTRACT(CDataString, 1);
      aFile = (CDataFile*)Factory_MakeData(CDataFile::GetRegisteredType());
    }

    // Environment stuff
    String Temp = Name;
    theAPI.m_ReturnStatus = theAPI.m_Engine->Environment->GetAbsolutePath(Temp, Name);
    if(!theAPI.m_ReturnStatus){
      return;
    }

		theAPI.m_ReturnStatus = aFile->Open(Name, Access);

    if(cnt == 3){
      CDataBool aBool;
      if(theAPI.m_ReturnStatus){
        aBool.Data = 1;
      }else{
        aBool.Data = 0;
      }

      theAPI.m_ReturnStatus = aVar->SetWorkingData(aFile);
      if(!theAPI.m_ReturnStatus){
        aBool.Data = 0;
      }

		  theAPI.m_ReturnStatus = theAPI.m_Engine->SetVariable(*aVar);
      if(!theAPI.m_ReturnStatus){
        aBool.Data = 0;
      }

      theAPI.m_ReturnStatus = STAT_OK;
      theAPI.SetReturnData(&aBool);
    }else{
      theAPI.SetReturnData(aFile);
      delete aFile;  // Wont get deleted later... dont need if cnt=3
      if(!theAPI.m_ReturnStatus){
        theAPI.m_ReturnStatus.Format(sERROR, ERR_P00102, Name.Str());
      }
    }
		return;
  }else	if(theAPI.m_FunctionName == "FindFile"){
		String sDir = SSI_EXTRACT(CDataString, 0);
    String sSearch = SSI_EXTRACT(CDataString, 1);

    sDir.Trim();
    String sRet;
    if(theAPI.m_Engine){
      if(theAPI.m_Engine->Environment){
        theAPI.m_ReturnStatus = theAPI.m_Engine->Environment->GetAbsolutePath(sDir, sRet);
        if(!theAPI.m_ReturnStatus){
          return;
        }
        if(sRet.GetLength() <= 0){
          sRet = theAPI.m_Engine->Environment->GetCWD();
        }
      }
    }

    CDataString Ret;
    theAPI.m_ReturnStatus = FindFile(sRet, sSearch, Ret.Data);
    theAPI.SetReturnData(&Ret);
		return;
	}else	if(theAPI.m_FunctionName == "MainWin"){
		String sTag;
		SSI_INT64 iTag = 0;
		String sClass;

		if(theAPI.GetParameterType(0) == CDataInt::GetRegisteredType()){
			iTag = SSI_EXTRACT(CDataInt, 0);
		}else{
			sTag = SSI_EXTRACT(CDataString, 0);
		}

		sClass = SSI_EXTRACT(CDataString, 1);

		CDataWindow* aWin = (CDataWindow*)Factory_MakeData(CDataWindow::GetRegisteredType());
		if(!aWin){
			theAPI.m_ReturnStatus = STAT_ERR;  // Internal Error
		}

		aWin->WinClass = sClass;
		aWin->WinName = "";
		aWin->WinTag = sTag;
		aWin->WinType = "MainWin";
		aWin->iWinTag = iTag;

		theAPI.SetReturnData(aWin);
		delete aWin;
		theAPI.m_ReturnStatus = STAT_OK;
		return;

	/////////////////////////////////////////////////////
	// Deprecated String Functions
	}else	if(theAPI.m_FunctionName == "GetLength"){
		CDataString s;
		CDataInt Ret;
		s.Data = SSI_EXTRACT(CDataString, 0);
		Ret.Data = s.Data.GetLength();
		theAPI.SetReturnData(&Ret);
		theAPI.m_ReturnStatus = STAT_OK;

	}else	if(theAPI.m_FunctionName == "Find"){
		CDataString s1, s2;
		CDataInt Ret;
		s1.Data = SSI_EXTRACT(CDataString, 0);
		s2.Data = SSI_EXTRACT(CDataString, 1);

		Ret.Data = s1.Data.Find(s2.Data);
		theAPI.SetReturnData(&Ret);
		theAPI.m_ReturnStatus = STAT_OK;

	}else	if(theAPI.m_FunctionName == "SubString"){
		CDataInt i1, i2;
		CDataString s1, Ret;
		s1.Data = SSI_EXTRACT(CDataString, 0);
		i1.Data = SSI_EXTRACT(CDataInt, 1);
		i2.Data = SSI_EXTRACT(CDataInt, 2);

		Ret.Data = s1.Data.SubString(i1.Data, i2.Data);
		theAPI.SetReturnData(&Ret);
		theAPI.m_ReturnStatus = STAT_OK;

	}else	if(theAPI.m_FunctionName == "Concat"){
		CDataString s1, s2;
		CDataString Ret;
		s1.Data = SSI_EXTRACT(CDataString, 0);
		s2.Data = SSI_EXTRACT(CDataString, 1);

		Ret.Data = s1.Data + s2.Data;
		theAPI.SetReturnData(&Ret);
		theAPI.m_ReturnStatus = STAT_OK;

	}else	if(theAPI.m_FunctionName == "IntToStr"){
		CDataInt i1;
		CDataString Ret;
		i1.Data = SSI_EXTRACT(CDataInt, 0);

		Ret.Data = i1.GetString();
		theAPI.SetReturnData(&Ret);
		theAPI.m_ReturnStatus = STAT_OK;

	}else	if(theAPI.m_FunctionName == "StrToInt"){
		CDataString s1;
		CDataInt Ret;
		s1.Data = SSI_EXTRACT(CDataString, 0);

		if(Ret.DataFromString(s1.Data)){
			theAPI.m_ReturnStatus = STAT_OK;
		}else{
			//theAPI.m_ReturnStatus = STAT_ERR; // Cannot convert string to int
      P_MAKEERR1(ERR_P00510, s1.Data.Str());
		}

		theAPI.SetReturnData(&Ret);

	}else	if(theAPI.m_FunctionName == "RealToStr"){
		CDataReal r1;
		CDataString Ret;
		r1.Data = SSI_EXTRACT(CDataReal, 0);

		Ret.Data = r1.GetString();
		theAPI.SetReturnData(&Ret);
		theAPI.m_ReturnStatus = STAT_OK;

	}else	if(theAPI.m_FunctionName == "StrToReal"){
		CDataString s1;
		CDataReal Ret;
		s1.Data = SSI_EXTRACT(CDataString, 0);

		if(Ret.DataFromString(s1.Data)){
			theAPI.m_ReturnStatus = STAT_OK;
		}else{
			//theAPI.m_ReturnStatus = STAT_ERR; // Cannot convert string to int
      P_MAKEERR1(ERR_P00511, s1.Data.Str());
		}

		theAPI.SetReturnData(&Ret);

	/////////////////////////////////////////////////////////////
	// Time Functions
	}else if(theAPI.m_FunctionName == "GetTime"){
		CDataString Ret;
		
		CString Time;
		SYSTEMTIME sTime;
		GetLocalTime(&sTime);
		Time.Format("%02d:%02d:%02d.%03d", sTime.wHour, sTime.wMinute, sTime.wSecond, sTime.wMilliseconds);
		Ret.Data = (LPCTSTR)Time.GetString();
		
		theAPI.m_ReturnStatus = STAT_OK;
		theAPI.SetReturnData(&Ret);
		return;
	}else if(theAPI.m_FunctionName == "GetDate"){
		CDataString Ret;

		CString Date;
		CTime aTime = CTime::GetCurrentTime();
		Date.Format("%02d\\%02d\\%04d", aTime.GetMonth(), aTime.GetDay(), aTime.GetYear());

		Ret.Data = (LPCTSTR)Date.GetString();
		theAPI.m_ReturnStatus = STAT_OK;
		theAPI.SetReturnData(&Ret);
		return;
	}else if(theAPI.m_FunctionName == "GetDayOfWeek"){
		CDataInt Ret;
		CTime aTime = CTime::GetCurrentTime();
		Ret.Data = aTime.GetDayOfWeek();

		theAPI.m_ReturnStatus = STAT_OK;
		theAPI.SetReturnData(&Ret);
		return;

	/////////////////////////////////////////////////////
	// Misc Functions
	}else if(theAPI.m_FunctionName == "TypeKeys"){

		CDataString s;
		s.Data = SSI_EXTRACT(CDataString, 0);
    CDataWindow W;
    theAPI.m_ReturnStatus = W.TypeKeys(NULL, s.Data);
		return;
	}else if(theAPI.m_FunctionName == "KeyUp"){

		CDataString s;
		s.Data = SSI_EXTRACT(CDataString, 0);
    CDataWindow W;
    theAPI.m_ReturnStatus = W.TypeKeys2(NULL, s.Data, 2);
		return;
	}else if(theAPI.m_FunctionName == "KeyDown"){

		CDataString s;
		s.Data = SSI_EXTRACT(CDataString, 0);
    CDataWindow W;
    theAPI.m_ReturnStatus = W.TypeKeys2(NULL, s.Data, 1);
		return;
		
	}else if(theAPI.m_FunctionName == "MouseClick"){
   	int X, Y, iType, iButton;
		iButton = SSI_EXTRACT(CDataInt, 0);
		X = SSI_EXTRACT(CDataInt, 1);
		Y = SSI_EXTRACT(CDataInt, 2);
		iType = SSI_EXTRACT(CDataInt, 3);
    theAPI.m_ReturnStatus = MouseClick(iType, X, Y, iButton);

		return;
 	}else if(theAPI.m_FunctionName == "MouseMove"){
 		int X, Y;
		X = SSI_EXTRACT(CDataInt, 0);
		Y = SSI_EXTRACT(CDataInt, 1);
    theAPI.m_ReturnStatus = MouseMove(X, Y);
		return;

	}else if(theAPI.m_FunctionName == "InputInt"){
		CDataString s;
		String Ret;
		s.Data = SSI_EXTRACT(CDataString, 0);
		if(!GetInput(s.Data, theAPI, Ret)){
			P_MAKEERR0(ERR_P00451)
			return;
		}
		CDataInt anInt;
		if(!anInt.DataFromString(Ret)){
			P_MAKEERR0(ERR_P00450)
			//theAPI.m_ReturnStatus = STAT_ERR;
			return;
		}
		theAPI.SetReturnData(&anInt);
		theAPI.m_ReturnStatus = STAT_OK;
		return;
	}else if(theAPI.m_FunctionName == "InputReal"){
		CDataString s;
		String Ret;
		s.Data = SSI_EXTRACT(CDataString, 0);
		if(!GetInput(s.Data, theAPI, Ret)){
			P_MAKEERR0(ERR_P00453)
			return;
		}
		CDataReal aReal;
		if(!aReal.DataFromString(Ret)){
			P_MAKEERR0(ERR_P00452)
			return;
		}
		theAPI.SetReturnData(&aReal);
		theAPI.m_ReturnStatus = STAT_OK;
		return;
	}else if(theAPI.m_FunctionName == "InputString"){
		CDataString s;
		String Ret;
		s.Data = SSI_EXTRACT(CDataString, 0);
		if(!GetInput(s.Data, theAPI, Ret)){
			P_MAKEERR0(ERR_P00454)
			return;
		}
		CDataString aString;
		aString.Data = Ret;
		theAPI.SetReturnData(&aString);
		theAPI.m_ReturnStatus = STAT_OK;
		return;
	}else if(theAPI.m_FunctionName == "PrintMessage"){
		CDataString s;
		s.Data = SSI_EXTRACT(CDataString, 0);
		theAPI.m_Engine->GetOutStream()->Disp(s.Data, 1);
		theAPI.m_ReturnStatus = STAT_OK;
		return;

	}else if(theAPI.m_FunctionName == "Alert"){
		CDataString s;
		s.Data = SSI_EXTRACT(CDataString, 0);
		MessageBeep(MB_ICONEXCLAMATION);
		MessageBox(SSI_NULL, s.Data.Str(), "Phantom Alert", MB_OK);
		theAPI.m_ReturnStatus = STAT_OK;
		return;
  }else if(theAPI.m_FunctionName == "LoadPackageDLL"){
		String Path = SSI_EXTRACT(CDataString, 0);

  	FUNCPROC Func;
    HMODULE id;
    SetLastError(0);
		id = LoadLibrary(Path.Str());
    DWORD dwErr = GetLastError();
		if(!id){
      P_MAKEERR1(ERR_P00553, Path.Str());
			return;
		}
	
    Func = (FUNCPROC)GetProcAddress(id, "RegisterFunctions");
	  if(!Func){
		  FreeLibrary(id);
		  id = 0;
      P_MAKEERR1(ERR_P00554, Path.Str());
		  return;
	  }
    (Func)(theAPI.m_Engine);
  }else if(theAPI.m_FunctionName == "PlaySound"){
		String Path = SSI_EXTRACT(CDataString, 0);
    CDataBool bRet;

    String Temp = Path;
    theAPI.m_ReturnStatus = theAPI.m_Engine->Environment->GetAbsolutePath(Temp, Path);
    if(!theAPI.m_ReturnStatus){
      return;
    }

    bRet.Data = PlaySound(Path.Str(), NULL, SND_FILENAME | SND_ASYNC);
    theAPI.SetReturnData(&bRet);
	/////////////////////////////////////////////////////////////
	// System Functions
	}else if(theAPI.m_FunctionName == "Run"){
		CDataString s;
		s.Data = SSI_EXTRACT(CDataString, 0);
		CDataBool Ret;

		if(!system(SSI_NULL)){
			P_MAKEERR0(ERR_P00254);
			return;
		}

    // Environment stuff  -- this will ruin OS commands
    String Temp = s.Data;
    theAPI.m_ReturnStatus = theAPI.m_Engine->Environment->GetAbsolutePath(Temp, s.Data);
    if(!theAPI.m_ReturnStatus){
      return;
    }

		int Val = system(s.Data.Str());
		if(Val < 0){
			Ret.Data = 0;
		}else{
			Ret.Data = 1;
		}
		theAPI.m_ReturnStatus = STAT_OK;
		theAPI.SetReturnData(&Ret);
		return;
	}else	if(theAPI.m_FunctionName == "System"){
		CDataString s;
		s.Data = SSI_EXTRACT(CDataString, 0);

    // Environment stuff  -- this will ruin OS commands
    String Temp = s.Data;
    theAPI.m_ReturnStatus = theAPI.m_Engine->Environment->GetAbsolutePath(Temp, s.Data);
    if(!theAPI.m_ReturnStatus){
      return;
    }

		STARTUPINFO Start;
		ZeroMemory(&Start, sizeof(Start));
		Start.cb = sizeof(Start);
		PROCESS_INFORMATION pInfo;
		if(!CreateProcess(SSI_NULL, (LPTSTR)s.Data.Str(), SSI_NULL, SSI_NULL, FALSE, SSI_NULL, SSI_NULL, SSI_NULL, &Start, &pInfo))
		{
			DWORD Err = GetLastError();
			if(Err == ERROR_FILE_NOT_FOUND)
			{
				theAPI.m_ReturnStatus.Format(sERROR, "System command '%s' failed", s.Data.Str());
				return;
			}
			else
			{
				theAPI.m_ReturnStatus.Format(sERROR, "System command '%s' failed", s.Data.Str());
				return;
			}			
		}
		Sleep(500);
		CloseHandle(pInfo.hThread);
		CloseHandle(pInfo.hProcess);
	}else	if(theAPI.m_FunctionName == "ChangeDir"){
		CDataString s;
		s.Data = SSI_EXTRACT(CDataString, 0);
    theAPI.m_ReturnStatus = theAPI.m_Engine->Environment->SetCWD(s.Data);
    if(!theAPI.m_ReturnStatus){
      return;
    }
		theAPI.m_ReturnStatus = STAT_OK;

	}else	if(theAPI.m_FunctionName == "GetMachineName"){
		CDataString Ret;

		char sName[TEXT_MAX];
		DWORD Size = TEXT_MAX;
		if(GetComputerName(sName, &Size)){
			Ret.Data = sName;
		}else{
      P_MAKEERR0(ERR_P00520);
			return;
		}		

		theAPI.m_ReturnStatus = STAT_OK;
		theAPI.SetReturnData(&Ret);
		return;
	}else	if(theAPI.m_FunctionName == "GetDirContents"){
		CDataString Ret;

		String Dir = SSI_EXTRACT(CDataString, 0);
		String Delim = SSI_EXTRACT(CDataString, 1);

    // Environment stuff
    String Temp = Dir;
    theAPI.m_ReturnStatus = theAPI.m_Engine->Environment->GetAbsolutePath(Temp, Dir);
    if(!theAPI.m_ReturnStatus){
      return;
    }

		WIN32_FIND_DATA aData;
		HANDLE h;

		h = FindFirstFile(Dir.Str(), &aData);
		if(h == INVALID_HANDLE_VALUE){
      P_MAKEERR1(ERR_P00521, Dir.Str());
			return;
		}
		Ret.Data = aData.cFileName;
		while(FindNextFile(h, &aData)){
			Ret.Data = Ret.Data + Delim + aData.cFileName;				
		}
		FindClose(h);	

		theAPI.m_ReturnStatus = STAT_OK;
		theAPI.SetReturnData(&Ret);
		return;
	}else	if(theAPI.m_FunctionName == "GetCurrentDir"){
		CDataString Ret;

    // Environment stuff
    Ret.Data = theAPI.m_Engine->Environment->GetCWD();

		theAPI.m_ReturnStatus = STAT_OK;
		theAPI.SetReturnData(&Ret);
		return;
	}else	if(theAPI.m_FunctionName == "MakeDir"){
		CDataBool Ret;
		Ret.Data = 0;

		String NewDir = SSI_EXTRACT(CDataString, 0);

    // Environment stuff
    String Temp = NewDir;
    theAPI.m_ReturnStatus = theAPI.m_Engine->Environment->GetAbsolutePath(Temp, NewDir);
    if(!theAPI.m_ReturnStatus){
      return;
    }

		if(CreateDirectory(NewDir.Str(), SSI_NULL)){
			Ret.Data = 1;
		}

		theAPI.m_ReturnStatus = STAT_OK;
		theAPI.SetReturnData(&Ret);
		return;
	}else	if(theAPI.m_FunctionName == "GetUserName"){
		CDataString Ret;

		char Buff[TEXT_MAX];
		int Size = TEXT_MAX;
		if(GetUserName(Buff, (LPDWORD)&Size)){
			Ret.Data = Buff;
		}else{
			DWORD Err = GetLastError();
			if(Err == 1245){
				//Error.Format(WRN00400);
				//LogWarning(Error);
				//Ret = TRUE;
			}
			theAPI.m_ReturnStatus = STAT_OK;
			return;
		}

		theAPI.m_ReturnStatus = STAT_OK;
		theAPI.SetReturnData(&Ret);
		return;
	}else	if((theAPI.m_FunctionName == "CopyFile") || (theAPI.m_FunctionName == "CopyDir")){
		CDataBool Ret;

		String From = SSI_EXTRACT(CDataString, 0);
		String To = SSI_EXTRACT(CDataString, 1);

    // Environment stuff
    String Temp = From;
    theAPI.m_ReturnStatus = theAPI.m_Engine->Environment->GetAbsolutePath(Temp, From);
    if(!theAPI.m_ReturnStatus){
      return;
    }

    // Environment stuff
    Temp = To;
    theAPI.m_ReturnStatus = theAPI.m_Engine->Environment->GetAbsolutePath(Temp, To);
    if(!theAPI.m_ReturnStatus){
      return;
    }

		int i = From.GetLength();
		char* sFrom = new char[i+2];
		sFrom = strcpy(sFrom, From.Str());
		sFrom[i] = 0;    // append another 0
		sFrom[i+1] = 0;  // append another 0, so ends with double NULLs

		i = To.GetLength();
		char* sTo = new char[i+2];
		sTo = strcpy(sTo, To.Str());
		sTo[i] = 0;    // append another 0
		sTo[i+1] = 0;  // append another 0, so ends with double NULLs


		SHFILEOPSTRUCT aStruct;
		aStruct.wFunc = FO_COPY;
		aStruct.pFrom = sFrom;
		aStruct.pTo = sTo;
		aStruct.fFlags = FOF_NOCONFIRMMKDIR | FOF_SILENT | FOF_NOCONFIRMATION | FOF_FILESONLY;
		if(SHFileOperation(&aStruct)){
		}else{
			Ret.Data = 1;
		}

		delete [] sFrom;
		delete [] sTo;

		theAPI.m_ReturnStatus = STAT_OK;
		theAPI.SetReturnData(&Ret);
		return;
	}else	if((theAPI.m_FunctionName == "DeleteFile") || (theAPI.m_FunctionName == "DeleteDir")){
		CDataBool Ret;

		String DelItem = SSI_EXTRACT(CDataString, 0);

		// Environment stuff
		String Temp = DelItem;
		theAPI.m_ReturnStatus = theAPI.m_Engine->Environment->GetAbsolutePath(Temp, DelItem);
		if(!theAPI.m_ReturnStatus){
			return;
		}

		int i = DelItem.GetLength();

		char* s = new char[i+2];
		s = strcpy(s, DelItem.Str());
		s[i] = 0;    // append another 0
		s[i+1] = 0;  // append another 0, so ends with double NULLs

		SHFILEOPSTRUCT aStruct;
		aStruct.wFunc = FO_DELETE;
		aStruct.pFrom = s;
		aStruct.pTo = "";
		aStruct.fFlags = FOF_SILENT | FOF_NOCONFIRMATION;
		if(SHFileOperation(&aStruct)){
		}else{
			Ret.Data = 1;
		}

		delete [] s;
		theAPI.m_ReturnStatus = STAT_OK;
		theAPI.SetReturnData(&Ret);

		return;
	}

}

void PhantomKeywordProcessor(SSI_KeywordAPI& theAPI){
	if(theAPI.m_Keyword == "use"){
		
		CStatus RetStat;
		CDataString aString;

    String sInput = theAPI.m_Data;
    RetStat = theAPI.m_Engine->GetKeywordStringInput(sInput);
    if(!RetStat){
      theAPI.m_Status = RetStat;
			return;
		}
		String LocFile;

    theAPI.m_Status = theAPI.m_Engine->Environment->GetAbsolutePath(sInput, LocFile);
    if(!theAPI.m_Status){
      return;
    }

		File aFile(LocFile);
    if(!aFile.Status){
      // This just returns a status OK or fail...
      //theAPI.m_Status = aFile.Status;
      theAPI.m_Status.Format(sERROR, ERR_P00103, LocFile.Str());
		  return;
    }
		DataNode CurrNode = 0;
		DataNode TopNode = 0;
		theAPI.m_Status = LoadDecNodes(aFile, CurrNode);
		if(!theAPI.m_Status){
			DelDataNodeTree(CurrNode);
			return;
		}
		TopNode = CurrNode;

		while(CurrNode){
			RetStat = AddNewWindows(CurrNode, theAPI.m_Engine);
			if(!RetStat){
				DelDataNodeTree(TopNode);
				theAPI.m_Status = RetStat;
				return;
			}
			CurrNode = CurrNode->Next;
		}

		theAPI.m_Status = STAT_OK;
		DelDataNodeTree(TopNode);
	}else	if(theAPI.m_Keyword == "define"){
		String sRight = theAPI.m_Data;
		String sLeft;
		CStatus RetStat;
		String Eq = "=";
		if(sRight.Parse(Eq, sLeft)<=0){
			theAPI.m_Status = CStatus(sERROR, ERR_P00211);;  // Incorrect define syntax
			return;
		}

		sLeft.Trim();
		sRight.Trim();

		// Handle cases in quotes
		CDataString s;
		if(s.DataFromString(sLeft)){
			sLeft = s.Data;
		}
		if(s.DataFromString(sRight)){
			sRight = s.Data;
		}

		int i = Phantom_MapWindow(sLeft, sRight);
		if(i<0){
			String s;
			s.Format(ERR_P00212, sLeft.Str(), sRight.Str());
			theAPI.m_Status = CStatus(sERROR, s);  // Could not map windows
			return;
		}
		theAPI.m_Status = STAT_OK;  // Incorrect define syntax
	}else	if(theAPI.m_Keyword == "dll"){
		String Rem = theAPI.m_Data;
		String Name;
		String Ret;
		String Temp;

		//StringTree Params;
		CStatus RetStat;
		SSI_INT64 Pos;

		CInterpreter* Interp = theAPI.m_Engine->GetInterpreter();
		if(!Interp){
			theAPI.m_Status = STAT_ERR;  // Internal Error
			return;
		}

		Pos = Rem.Parse("(", Ret);
		if(Pos < 0){
      theAPI.m_Status.Format(sERROR, ERR_P00530);
			return;
		}

		Name = Ret;

		Rem.Reverse();
		Pos = Rem.Parse(")", Ret);
		if(Pos < 0){
      theAPI.m_Status.Format(sERROR, ERR_P00531);
			return;
		}
		
		Rem.Reverse();
		
		String DllFile;
		String DllFunc;
		String RetType;
		CVariable aVar;

		Pos = Rem.Parse(",", DllFile);
		if(Pos < 0){
      theAPI.m_Status.Format(sERROR, ERR_P00532);
			return;
		}
		RetStat = Interp->Evaluate(DllFile, aVar);
		if(!RetStat){
			theAPI.m_Status = RetStat;  // require a string
			return;
		}
		if(aVar.GetType() != CDataString::GetRegisteredType()){
      theAPI.m_Status.Format(sERROR, ERR_P00533, 1);
			return;
		}
		DllFile = ((CDataString*)&(aVar.GetWorkingData()))->Data.Str();
    
    // New environment processing
    String LocDLLFile;
    theAPI.m_Status = theAPI.m_Engine->Environment->GetAbsolutePath(DllFile, LocDLLFile);
    if(!theAPI.m_Status){
      return;
    }
    DllFile = LocDLLFile;

		Pos = Rem.Parse(",", DllFunc);
		if(Pos < 0){
      theAPI.m_Status.Format(sERROR, ERR_P00534);
			return;
		}
  	RetStat = Interp->Evaluate(DllFunc, aVar);
		if(!RetStat){
			theAPI.m_Status = RetStat;  // require a string
			return;
		}
		if(aVar.GetType() != CDataString::GetRegisteredType()){
      theAPI.m_Status.Format(sERROR, ERR_P00533, 2);
			return;
		}
		DllFunc = ((CDataString*)&(aVar.GetWorkingData()))->Data.Str();

		Pos = Rem.Parse(",", Temp);  // Junk (false parameter)
		if(Pos < 0){
      theAPI.m_Status.Format(sERROR, ERR_P00535);
			return;
		}

		Pos = Rem.Parse(",", RetType);
		if(Pos < 0){// No parameters
      RetType = Rem;
      Rem = "";
    }

		RetType.Trim();
		SSI_INT64 iRetType = Interp->GetVariableType(RetType);
		if(!iRetType){
      theAPI.m_Status.Format(sERROR, ERR_P00536);
			return;
		}

		CFunctionDecl aNewFunc;
		aNewFunc.Clear();
		aNewFunc.Name = Name;
		aNewFunc.ReturnType = iRetType;
		
		String sVar;
		String sType;
		SSI_INT64 iType = 0;
		char bGo = 1;
		do{
			Rem.Trim();
			Pos = Rem.Parse(",", Temp);
			Temp.Trim();
			if(Pos < 0){
				bGo = 0;
				Temp = Rem;
			}

			Pos = Temp.Parse(" ", sType);
			if(Pos <= 0){
				sType = Temp;
				sVar = "";
			}else{
				sVar = Temp;
			}
			sType.Trim();
  
      if(sType.GetLength() == 0){
        break;
      }

			iType = Interp->GetVariableType(sType);
			if(!iType){
        theAPI.m_Status.Format(sERROR, ERR_P00537, sType.Str());
				return;
			}
			aNewFunc.addType(iType);
		}while(bGo);

    
		aNewFunc.SetProcessor((ProcessingFunctionPtr)DllFunctionProcessor);
    theAPI.m_Engine->SetPackageName("User Defined");
		RetStat = theAPI.m_Engine->AddFunction(aNewFunc);
		if(!RetStat){
			theAPI.m_Status = RetStat;  // Could not add function
			return;
		}

		CDllFunc NewDll;
		NewDll.DllFileName = DllFile;
		NewDll.DllFuncName = DllFunc;
		NewDll.FuncName = Name;
		NewDll.RetType = iRetType;

		PHANTOM_DllList.push(NewDll);
		theAPI.m_Status = STAT_OK;  // Incorrect define syntax
	}
}

CStatus AddNewWindows(DataNode CurrNode, CEngine* theEngine){
	CStatus RetStat;

	if(*(CurrNode->WinType) == "MainWin" || *(CurrNode->WinType) == "Menu"){
		CDataWindow* aWin = (CDataWindow*)Factory_MakeData(CDataWindow::GetRegisteredType());
		if(!aWin) return STAT_ERR;  // Internal Error

		aWin->WinClass = *CurrNode->WinClass;
		aWin->WinName = *CurrNode->WinName;
		aWin->WinTag = *CurrNode->WinTag;
		aWin->WinType = *CurrNode->WinType;
		if(CurrNode->FChild){
			RetStat = AddChildren(CurrNode->FChild, aWin, theEngine);
			if(!RetStat) return RetStat;
		}
		RetStat = theEngine->AddVariable(*aWin, aWin->WinName);
		delete aWin;
	}else{
		// This is assuming it is a ClassWindow type

		CFunction F;
		CVariable Par1;
		CVariable Par2;
		CDataString sData;
		if(CurrNode->iWinTag){
			Par1.SetType(CDataInt::GetRegisteredType());
			CDataInt D;
			D.Data = CurrNode->iWinTag;
			Par1.SetData(&D);
		}else{
			Par1.SetType(CDataString::GetRegisteredType());
			sData.Data = *CurrNode->WinTag;
			Par1.SetData(&sData);
		}

		sData.Data = *CurrNode->WinClass;
		Par2.SetType(CDataString::GetRegisteredType());
		Par2.SetData(&sData);
		F.AddParam(Par1);
		F.AddParam(Par2);
		F.Name = CurrNode->WinType->Str();
		CStatus RetStat = theEngine->DoFunction(F);
		if(!RetStat){
			return STAT_ERR;  // Should come up with another error (not RetStat)
		}

		CVariable RetVar = F.GetReturn();
		CDataWindow* aWin = (CDataWindow*)&RetVar.GetWorkingData();
		aWin->WinClass = *CurrNode->WinClass;
		aWin->WinName = *CurrNode->WinName;
		aWin->WinTag = *CurrNode->WinTag;
		aWin->WinType = *CurrNode->WinType;

		if(CurrNode->FChild){
			RetStat = AddChildren(CurrNode->FChild, aWin, theEngine);
			if(!RetStat) return RetStat;
		}

		RetStat = theEngine->AddVariable(RetVar.GetWorkingData(), *CurrNode->WinName);
		return RetStat;

	}	
	return RetStat;
}

CStatus AddChildren(DataNode CurrNode, CDataWindow* ParWin, CEngine* theEngine){

	CStatus RetStat;
	while(CurrNode){

    // OK, so it looks like for top-level menus, (Like Notepad._File), wont construct a correct path here, like for lower level menus.  
    // Can fix this in FindMe, but it should be fixed here, I think...

		if(*CurrNode->WinType == "Menu" && (*CurrNode->WinName == "NoName" || *CurrNode->WinName == "Menu") && *CurrNode->WinClass == "Menu" && *CurrNode->WinTag == ""){
			if(!CurrNode->FChild){
				// Menu keyword must have children
				MAKEERR0(ERR_P00212);
			}
			RetStat = AddChildren(CurrNode->FChild, ParWin, theEngine);
			CurrNode = CurrNode->Next;
        }else if(CurrNode->WinType->GetLength() <= 0) {
            CurrNode = CurrNode->Next;  // This is for declaration files with empty declarations, as occurs some times
		}else{

			if(0){
				CFunction F;
				CVariable Par1;
				CVariable Par2;
				CDataString sData;
				F.Name = *CurrNode->WinType;
				if(*CurrNode->WinType == "Menu" || *CurrNode->WinType == "MenuItem"){   // Kludge menu since it is 0 based
					F.Name = "MainWin";
					Par1.SetType(CDataInt::GetRegisteredType());
					CDataInt D;
					D.Data = CurrNode->iWinTag;
					Par1.SetData(&D);
				}else if(CurrNode->iWinTag){
					Par1.SetType(CDataInt::GetRegisteredType());
					CDataInt D;
					D.Data = CurrNode->iWinTag;
					Par1.SetData(&D);
				}else{
					Par1.SetType(CDataString::GetRegisteredType());
					sData.Data = *CurrNode->WinTag;
					Par1.SetData(&sData);
				}

				sData.Data = *CurrNode->WinClass;
				Par2.SetType(CDataString::GetRegisteredType());
				Par2.SetData(&sData);
				F.AddParam(Par1);
				F.AddParam(Par2);
				
				CStatus RetStat = theEngine->DoFunction(F);
				if(!RetStat){
					return STAT_ERR;  // Should come up with another error (not RetStat)
				}
				CVariable RetVar = F.GetReturn();
				CDataWindow* Child = (CDataWindow*)&RetVar.GetWorkingData();
				Child->WinClass = *CurrNode->WinClass;
				Child->WinName = *CurrNode->WinName;
				Child->WinTag = *CurrNode->WinTag;
				Child->WinType = *CurrNode->WinType;

				((CDataWindow*)ParWin)->ConstructChildPath(Child);

				if(CurrNode->FChild){
					RetStat = AddChildren(CurrNode->FChild, Child, theEngine);
					if(!RetStat) return RetStat;
				}

				// So, it has to be derived from CDataWindow
				((CDataWindow*)ParWin)->AddLocalVariable(Child, *CurrNode->WinName);

				if(!RetStat){
					return RetStat;
				}

			}else	if(0){

				CFunctionDecl aDecl;
				SSI_FunctionAPI theAPI;

				CFunction F;
				CVariable Par1;
				CVariable Par2;
				CDataString sData;
				if(CurrNode->iWinTag || *CurrNode->WinType == "Menu" || *CurrNode->WinType == "MenuItem"){   // Kludge menu since it is 0 based
					Par1.SetType(CDataInt::GetRegisteredType());
					CDataInt D;
					D.Data = CurrNode->iWinTag;
					Par1.SetData(&D);
				}else{
					Par1.SetType(CDataString::GetRegisteredType());
					sData.Data = *CurrNode->WinTag;
					Par1.SetData(&sData);
				}

				Par1.Name = *CurrNode->WinName;
				sData.Data = *CurrNode->WinClass;
				Par2.SetType(CDataString::GetRegisteredType());
				Par2.SetData(&sData);
				F.AddParam(Par1);
				F.AddParam(Par2);
				F.Name = CurrNode->WinType->Str();

				theAPI.m_Engine = theEngine;
				RetStat = ParWin->FuncSet->CheckFunction(F, theAPI, aDecl);
				if(!RetStat) return RetStat;
				ParWin->SSI_FunctionProcessor(theAPI);

				if(!theAPI.m_ReturnStatus){
					return STAT_ERR;  // Make another error
				}	
				CVariable* RetVar = theAPI.GetReturnData();
				CDataWindow* Child = (CDataWindow*)&RetVar->GetWorkingData();

				if(CurrNode->FChild){
					RetStat = AddChildren(CurrNode->FChild, Child, theEngine);
					if(!RetStat) return RetStat;
				}
				RetVar->Name = *CurrNode->WinName;
				RetVar->SetWorkingData(Child);
				if(!RetStat){
					return RetStat;
				}
			}else{

				CDataWindow* aWin = (CDataWindow*)Factory_MakeData(CDataWindow::GetRegisteredType());
				if(!aWin) return STAT_ERR;  // Internal Error

				aWin->WinClass = *CurrNode->WinClass;
				aWin->WinName = *CurrNode->WinName;
				aWin->WinTag = *CurrNode->WinTag;
				aWin->WinType = *CurrNode->WinType;
				aWin->iWinTag = CurrNode->iWinTag;

				((CDataWindow*)ParWin)->ConstructChildPath(aWin);

				if(CurrNode->FChild){
					RetStat = AddChildren(CurrNode->FChild, aWin, theEngine);
					if(!RetStat) return RetStat;
				}

				RetStat = ParWin->AddLocalVariable(aWin, aWin->WinName);
				delete aWin;
				if(!RetStat) return RetStat;
			}
			CurrNode = CurrNode->Next;
		}

	}
	return STAT_OK;
}

////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
void DelDataNodeTree(DataNode winNode)
{
	if(winNode)
	{
		if(winNode->WinName) delete winNode->WinName;
		if(winNode->WinTag) delete winNode->WinTag;
		if(winNode->WinClass) delete winNode->WinClass;
		if(winNode->WinType) delete winNode->WinType;
		if(winNode->Next)
		{
			DelDataNodeTree(winNode->Next);
		}
		if(winNode->FChild)
		{
			DelDataNodeTree(winNode->FChild);
		}
		delete winNode;
	}
}

DataNode GetFreshNode(DataNode Ret)
{
	if(Ret)
	{
		DelDataNodeTree(Ret);
	}
	Ret = new WinDataT;
	Ret->WinName = new String;
	Ret->WinType = new String;
	Ret->WinTag = new String;
	Ret->WinClass = new String;
	
	Ret->IsParent = 0;
	Ret->IsChild = 0;
	Ret->iWinTag = 0;
	Ret->Parent = SSI_NULL;
	Ret->FChild = SSI_NULL;
	Ret->Prev = SSI_NULL;
	Ret->Next = SSI_NULL;
	return Ret;	
}




CStatus LoadDecNodes(File& InFile, DataNode &TopNode)
{
	int NumTabs;
	int Pos;
	int Move;
	int ctr;
	int CurrPos = -1;

	if(!InFile.Status){
		return InFile.Status;
	}
		
	CDataString aString;
	CDataInt anInt;

	DataNode CurrNode = GetFreshNode(0);
	TopNode = CurrNode;

	String DType;
	String Line;
	char OBracket = '[';
	char CBracket = ']';
	char OParen = '(';
	char CParen = ')';
	char Equal = '=';
	char Space = ' ';

	int LineCtr = 0;

	String Tok;
	String RetParse;

	while(InFile.ReadLine(Line) >= 0)
	{
		LineCtr++;
		NumTabs = CountTabs(Line);

		// In case there is a [] or () in a tag/class name
		Line.SetQuoteState(1);
		Tok.SetAt(0, OBracket);
		Pos = Line.Parse(Tok, RetParse);

		if(Pos != -1)
		{
			if(CurrPos == -1)
			{
			}
			else if(NumTabs > CurrPos)
			{
				Move = NumTabs - CurrPos;
				for(ctr = 0; ctr < Move; ctr++)
				{
					CurrNode->FChild = GetFreshNode(CurrNode->FChild);
					CurrNode->FChild->Parent = CurrNode;
					CurrNode = CurrNode->FChild;
				}
			}
			else if(NumTabs < CurrPos)
			{
				Move = CurrPos - NumTabs;
				for(ctr = 0; ctr < Move; ctr++)
				{
					CurrNode = CurrNode->Parent;
				}
				CurrNode->Next = GetFreshNode(CurrNode->Next);
				CurrNode->Next->Parent = CurrNode->Parent;
				CurrNode->Next->Prev = CurrNode;
				CurrNode = CurrNode->Next;
			}
			else
			{
				CurrNode->Next = GetFreshNode(CurrNode->Next);
				CurrNode->Next->Parent = CurrNode->Parent;
				CurrNode->Next->Prev = CurrNode;
				CurrNode = CurrNode->Next;
			}
			CurrPos = NumTabs;
			
			Tok.SetAt(0, CBracket);
			Pos = Line.Parse(Tok, RetParse);
			if(Pos < 0){
				MAKEERR1(ERR_P00250, LineCtr);
			}

			Tok.SetAt(0, Space);
			Pos = Line.Parse(Tok, RetParse);
			if(Pos < 0){
				if(Line == "Menu"){
					RetParse = "Menu";
					Line = "\"Menu\"";  // TEMPORARY FIX			
				}else{
					MAKEERR1(ERR_P00251, LineCtr);
				}
			}

			*CurrNode->WinType = RetParse;
			
			CDataString aString;
			aString.DataFromString(Line);
			*CurrNode->WinName = aString.Data;

      CurrNode->WinName->Trim();
      if(CurrNode->WinName->GetLength() <= 0){
				*CurrNode->WinName = "NoName";
			}
		}
		Tok.SetAt(0, OParen);
		Pos = Line.Parse(Tok, RetParse);
		if(Pos != -1)
		{
			Tok.SetAt(0, CParen);
			Pos = Line.Parse(Tok, RetParse);
			if(Pos < 0){
        MAKEERR0(ERR_P00540)
			}
			Tok.SetAt(0, Equal);
			Pos = Line.Parse(Tok, RetParse);
			DType = RetParse;

			if(DType == "Tag")
			{
				if(aString.DataFromString(Line)){
					*CurrNode->WinTag = aString.Data;
				}else if(anInt.DataFromString(Line)){
					CurrNode->iWinTag = anInt.Data;  // Change this
				}
			}
			else if(DType == "Class")
			{
				if(aString.DataFromString(Line)){
					*CurrNode->WinClass = aString.Data;
				}else if(anInt.DataFromString(Line)){
				}
			}
			else if(DType == "Parent")
			{
				CDataInt anInt;
				if(!anInt.DataFromString(Line)){
					// Syntax error
					MAKEERR1(ERR_P00252, LineCtr);
				}
				CurrNode->IsParent = anInt.Data;
			}
			else if(DType == "Child")
			{
				CDataInt anInt;
	 			if(!anInt.DataFromString(Line)){
					// Syntax error
					MAKEERR1(ERR_P00253, LineCtr);
				}

				CurrNode->IsChild = anInt.Data;
			}

      // Handle that cursed Menu decision I made ages ago
      // Note that WinClass is never set because it is not in quotes, so it is ""
      // Actually, I dont think we need this... in fact, it breaks things as is
      // Notepad with a menu will set the Notepad class to menu.  It seems that at
      // the very least, this is in the wrong place, but it doesnt seem to be needed.
      /*
      if(*CurrNode->WinType == "Menu" && (*CurrNode->WinName == "Menu" || *CurrNode->WinName == "NoName") && *CurrNode->WinClass == "" && *CurrNode->WinTag == ""){
        if(CurrNode->Prev){
          CurrNode = CurrNode->Prev;
          delete CurrNode->Next;
          CurrNode->Next = NULL;
        }else if(CurrNode->Parent){
          CurrNode = CurrNode->Parent;
          delete CurrNode->FChild;
          CurrNode->FChild = NULL;
        }
      }
      */

		}
	}
	return STAT_OK;
}

DataNode CopyPath(DataNode Original)
{
	DataNode Ret = SSI_NULL;
	if(!Original) return SSI_NULL;
	Ret = GetFreshNode(Ret);
	*Ret->WinName = *Original->WinName;
	*Ret->WinType = *Original->WinType;
	*Ret->WinTag = *Original->WinTag;
	*Ret->WinClass = *Original->WinClass;
	Ret->iWinTag = Original->iWinTag;
	Ret->IsChild = Original->IsChild;
	Ret->IsParent = Original->IsParent;
	Ret->Parent = Original->Parent;
	if(Original->FChild){
		Ret->FChild = CopyPath(Original->FChild);
	}
	return Ret;
}

SSI_BOOL GetInput(String& Prompt, SSI_FunctionAPI& theAPI, String& Ret){
	// Also provide ability to handle via window input
	// Actually, may be already handled via Engine's input
	char str[4096];
	str[4095] = 0;

	CEngine* anEng;
	anEng = theAPI.m_Engine;
	if(!anEng) return 0;
	ssi_stream* out = anEng->GetOutStream();
	if(!out){
		return 0;
	}
	out->Disp(Prompt.Str());
	out->getline(str, 4095);
	Ret = str;
	return 1;
}

void DllFunctionProcessor(SSI_FunctionAPI& theAPI){
	CDllFunc aDll;
	aDll.FuncName = theAPI.m_FunctionName;

	DllNode* aNode = PHANTOM_DllList.find(aDll);
	aNode->Get(aDll);
	if(!aNode){
		theAPI.m_ReturnStatus = STAT_ERR;  // Internal error... cannot find DLL function name
		return;
	}

	HMODULE hDll = SSI_NULL;
	
	DWORD dwParams[MAX_DLL_PARAMS]; //= SSI_NULL;

	SSI_INT64 NumParams = theAPI.GetNumParams();

	if(NumParams > MAX_DLL_PARAMS){
    P_MAKEERR1(ERR_P00538, MAX_DLL_PARAMS)
		return;
	}

	SSI_INT64 ctr;
	CData* aData = 0;
	for(ctr = 0; ctr < NumParams; ctr++){
		aData = *theAPI[ctr];
		if(!aData){
			theAPI.m_ReturnStatus = STAT_ERR;  // Internal error... cannot find all parameters
			return;
		}
		if(aData->Type == CDataInt::GetRegisteredType()){
			dwParams[ctr] = (DWORD)((CDataInt*)aData)->Data;
		}else if(aData->Type == CDataString::GetRegisteredType()){
			dwParams[ctr] = reinterpret_cast<DWORD>(((CDataString*)aData)->Data.Str());
		}else if(aData->Type == CDataBool::GetRegisteredType()){
			dwParams[ctr] = (DWORD)((CDataBool*)aData)->Data;
		}else{
      P_MAKEERR1(ERR_P00539, ctr)
			return;
		}
	}

	if((hDll = (HMODULE)LoadLibrary(aDll.DllFileName.Str())) == SSI_NULL)
	{
		theAPI.m_ReturnStatus = STAT_ERR;  // Unacceptable DLL parameter
    P_MAKEERR1(ERR_P00550, aDll.DllFileName.Str())
		return;	
	}

	// BASED UPON 16 PARAMS //
	typedef DWORD (*LPFUNC)(DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD, DWORD);

	LPFUNC pAddr;
	if((pAddr = (LPFUNC)GetProcAddress(hDll, aDll.DllFuncName.Str())) == SSI_NULL)
	{
    P_MAKEERR1(ERR_P00551, aDll.DllFuncName.Str())
		return;	
	}
	
	DWORD dwRet;
	dwRet = pAddr(dwParams[0], dwParams[1], dwParams[2], dwParams[3], dwParams[4], dwParams[5], dwParams[6], dwParams[7],dwParams[8], dwParams[9],dwParams[10], dwParams[11],dwParams[12], dwParams[13],dwParams[14], dwParams[15]);


	if(aDll.RetType == CDataInt::GetRegisteredType()){
		CDataInt aData;
		aData.Data = (int)dwRet;
		theAPI.SetReturnData(&aData);
	}else if(aDll.RetType == CDataBool::GetRegisteredType()){
		CDataBool aData;
		aData.Data = (SSI_BOOL)dwRet;
		theAPI.SetReturnData(&aData);
	}else if(aDll.RetType == CDataString::GetRegisteredType()){
		CDataString aData;
		aData.Data = reinterpret_cast<char*>(dwRet);
		theAPI.SetReturnData(&aData);
  }else if(aDll.RetType == CDataVoid::GetRegisteredType()){
	}else{
		FreeLibrary(hDll);
    P_MAKEERR0(ERR_P00552)
		return;	
	}

	theAPI.m_ReturnStatus = STAT_OK;
	FreeLibrary(hDll);

}

CStatus FindFile(String& Dir, String &InFile, String &OutFile){
    struct _finddata_t c_file;
    long hFile;

    Dir.Trim();
    InFile.Trim();
    int iLen = Dir.GetLength();
    if(iLen > 0){
      if(Dir.GetAt(iLen-1) != '\\' && Dir.GetAt(iLen-1) != '/'){
        Dir = Dir + "\\";
      }
    } 

    String s = Dir + InFile;
    OutFile = "";
    if( (hFile = _findfirst(s.Str() , &c_file )) == -1L ){
      return STAT_OK;
    }
    OutFile = Dir + c_file.name;
    _findclose( hFile );
    return STAT_OK;
}